package com.chenfan.mcn.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chenfan.ccp.common.result.R;
import com.chenfan.ccp.util.start.ApplicationContextUtil;
import com.chenfan.common.vo.Response;
import com.chenfan.common.vo.UserVO;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.exception.RpcResultException;
import com.chenfan.common.extension.context.TraceContext;
import com.chenfan.common.extension.context.TraceContextHolder;
import feign.*;
import feign.codec.Decoder;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.optionals.OptionalDecoder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;

/**
 * FeignClient配置
 *
 * @author wulg
 * @date 2021-09-24
 **/
@Configuration
public class FeignClientConfig {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;


    /**
     * 配置 增强Feign解码器
     * @return
     */
    @Bean
    public Decoder feignDecoder() {
        return new OptionalDecoder(new EnhancedFeignDecoder(new SpringDecoder(this.messageConverters)));
    }

    @Bean
    public FeignTokenRequestInterceptor feignTokenRequestInterceptor() {
        return new FeignTokenRequestInterceptor();
    }


    /**
     * 增强Feign编码器 自定义
     */
    @Slf4j
    public static class EnhancedFeignEncoder implements Encoder {

        private final Encoder delegate;

        public EnhancedFeignEncoder(final Encoder delegate) {
            this.delegate = delegate;
        }

        @Override
        public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
            delegate.encode(o, type, requestTemplate);
        }
    }

    /**
     * 增强Feign解码器 自定义
     */
    @Slf4j
    public static class EnhancedFeignDecoder extends ResponseEntityDecoder {

        public EnhancedFeignDecoder(final Decoder decoder) {
            super(decoder);
        }

        @Override
        public Object decode(final feign.Response response, Type type)
                throws IOException, FeignException {
            Object result = super.decode(response, type);
            if(result != null && result instanceof Response) {
                Response data = ((Response) result);
                handleResult(response.request(), data.getCode(), data.getMessage(), result);
            }
            if(result != null && result instanceof R) {
                R data = ((R) result);
                handleResult(response.request(), data.getCode(), data.getMessage(), result);
            }
            return result;
        }

        /**
         * 处理client响应结果
         * @param result
         */
        @SneakyThrows
        public void handleResult(Request request, int code , String message, Object result) {
            String httpMethod = request.httpMethod().name();
            URI uri = new URI(request.url());
            String path = uri.getRawPath();
            RequestTemplate requestTemplate = request.requestTemplate();
            String serviceId = ((Target) Optional.ofNullable(requestTemplate.feignTarget()).get()).name();
            if(code == McnErrorCode.SUCCESS.code() || code== McnErrorCode.SUCCESS_OLD.code()) {
                if(!ApplicationContextUtil.isProd() && log.isDebugEnabled()) {
                    log.debug("调微服务={} ---> {} {} 请求={}，响应={} <--- 操作成功！", serviceId, httpMethod, path, this.getRequestParam(request), JSON.toJSONString(result));
                }
                return;
            }
            log.error("调微服务={} ---> {} {} 请求={}，响应={} <--- 操作失败！", serviceId, httpMethod, path, this.getRequestParam(request), JSON.toJSONString(result));
            throw new RpcResultException(serviceId, httpMethod, path, code, message);
        }

        /**
         *  获取请求入参
         * @return
         */
        private String getRequestParam(Request request) {
            Request.HttpMethod httpMethod = request.httpMethod();
            String reqParam = "";
            switch (httpMethod) {
                case POST:
                    if(ArrayUtils.isNotEmpty(request.body()) && Objects.nonNull(request.charset())) {
                        reqParam = new String(request.body(), request.charset());
                    }
                    break;
                case GET:
                    reqParam = JSONObject.toJSONString(request.requestTemplate().queries());
                default:
            }
            return reqParam;
        }
    }

    /**
     * Feign调用透传Token
     *
     * @author wulg
     * @date 2022-03-21
     **/
    @Slf4j
    public static class FeignTokenRequestInterceptor implements RequestInterceptor {

        @SneakyThrows
        @Override
        public void apply(RequestTemplate requestTemplate) {
            String token = TraceContextHolder.get(TraceContext.TOKEN);
            if (StringUtils.isNotBlank(token)) {
                requestTemplate.header(TraceContext.TOKEN, token);
            }
            String appKey = TraceContextHolder.get(TraceContext.APP_KEY);
            if (StringUtils.isNotBlank(appKey)) {
                requestTemplate.header(TraceContext.APP_KEY, appKey);
            }
            Long tenantId = UserVoContextHolder.getTenantId();
            if (Objects.nonNull(tenantId)) {
                requestTemplate.header(TraceContext.TENANT_ID, tenantId.toString());
            }
            //如果Mock的话，通过Nginx，请求头大小限制报400，暂时注释掉
            UserVO userVO = TraceContextHolder.get(TraceContext.CURRENT_USER);
            if(Objects.nonNull(userVO)) {
//                String encodedUserInfo = URLEncoder.encode(JSONObject.toJSONString(userVo), "UTF-8");
//                if(log.isDebugEnabled())  {
//                    log.debug("Feign透传经鉴权后的用户信息！用户ID：{}", userVo.getUserId());
//                }
//                requestTemplate.header(TraceContext.CURRENT_USER, encodedUserInfo);
            }
        }
    }
}
